import { CharStreams, CommonTokenStream } from 'antlr4ts';
import { CymbolLexer } from './CymbolLexer';
import { BlockContext, ClassDefinitionContext, ClassMemberContext, CymbolParser, FormalParmetersContext, MethodDeclarationContext, PrimaryContext,StructDeclarationContext,StructMemberContext,VarDeclarationContext } from './CymbolParser';
let input = `
// start of global scope    // <callout id="co.class-symtab-inherit-globals"/>
// implicit class Object { int hashCode() {...} }  // <callout id="co.class-symtab-inherit-Object"/>
class A {					// <callout id="co.class-symtab-inherit-A"/>
public:
 int x;
 void foo()					// <callout id="co.class-symtab-inherit-A-foo"/>
 { ; }						// <callout id="co.class-symtab-inherit-A-foo1"/>
 void bar()					// <callout id="co.class-symtab-inherit-A-bar"/>
 { ; }						// <callout id="co.class-symtab-inherit-A-bar1"/>
};
class B : public A {		// <callout id="co.class-symtab-inherit-B"/>
 int y;
 void foo()					// <callout id="co.class-symtab-inherit-B-foo"/>
 {// <callout id="co.class-symtab-inherit-B-foo1"/>
   this.x = this.y;
   bar(); // invoke A::bar()
 }
};
`

// Create the lexer and parser
let inputStream = CharStreams.fromString(input);
let lexer = new CymbolLexer(inputStream);
let tokenStream = new CommonTokenStream(lexer);
let parser = new CymbolParser(tokenStream);
// AST
let tree = parser.compilationUnit();
// 符号表

import { ClassSymbol, LocalScope, MethodSymbol, Scope, StructSymbol, SymbolTable, Type, VariableSymbol, _Symbol } from '.';
// 监听器模式

import { ParseTreeWalker } from 'antlr4ts/tree/ParseTreeWalker'
import { CymbolListener } from './CymbolListener';
import { ParseTreeListener } from "antlr4ts/tree/ParseTreeListener";
import { TerminalNode } from 'antlr4ts/tree/TerminalNode';

class myListener implements CymbolListener {
    symTab:SymbolTable;
    currentScope:Scope | null;
    
    constructor(){
        this.symTab = new SymbolTable();
        this.currentScope = this.symTab.globals
    }

    // 变量声明
    enterVarDeclaration(ctx:VarDeclarationContext){
         //检查是否赋值表达式
         let line = ctx.ID().symbol.line;
         console.log("line " + line + " def " + ctx.ID().text);
         let varType = this.symTab.globals.resolve(ctx.type().text);
         let vs = new VariableSymbol(ctx.ID().text, varType as Type);
         this.currentScope?.define(vs);

         //赋值表达式
         if (ctx.expression()) {
             console.log("line " + line + " assign to " + ctx.ID().text);
         }
    }
    // 变量引用
    enterPrimary(ctx:PrimaryContext){
        if(ctx.ID()){
            let line = (ctx.ID() as TerminalNode).symbol.line;
            let idText = (ctx.ID() as TerminalNode).text
            let idVarSym = this.currentScope?.resolve(idText);

            console.log("line " + line + ':' + idText + " ref to " +idVarSym?.toString())
        }
    }
    // 函数声明
    enterMethodDeclaration(ctx:MethodDeclarationContext){
        let line = ctx.ID().symbol.line;
        console.log("line " + line + " def method " + ctx.ID().text);

        //返回类型
        let retType = this.symTab.globals.resolve(ctx.type().text);
        if(retType){
            let ms = new MethodSymbol(
                ctx.ID().text,
                retType as Type,
                this.currentScope as Scope
            )
            this.currentScope?.define(ms);
            this.currentScope = ms;
        }else{
            console.log("type error", ctx.type().text);
        }
    }
    // 参数处理
    enterFormalParmeters(ctx:FormalParmetersContext){
        ctx.ID().forEach((f,i) => {
            let fType = this.symTab.globals.resolve(ctx.type(i).text);

            this.currentScope?.define(
                new _Symbol(ctx.ID(i).text as string,fType)
            )
        })
    }
    exitMethodDeclaration(ctx:MethodDeclarationContext){
        this.currentScope = (this.currentScope as Scope).getEnclosingScope();
    }
    // 局部作用域
    enterBlock(ctx:BlockContext){
        this.currentScope = new LocalScope(this.currentScope as Scope)
    }
    exiterBlock(ctx:BlockContext){
        console.log("locals:" + this.currentScope?.getScopeName());
        this.currentScope = (this.currentScope as Scope).getEnclosingScope();
    }

    // 结构体声明
    enterStructDeclaration(ctx:StructDeclarationContext){
        let line = ctx.ID().symbol.line;
        console.log("line " + line + " def struct " + ctx.ID().text);

        let ss = new StructSymbol(
            ctx.ID().text,
            this.currentScope as Scope
        )
        this.currentScope?.define(ss);
        this.currentScope = ss;
    }   

    enterStructMember(ctx:StructMemberContext){
        if(ctx.ID()){
            // struct {int a}
            let line = ctx.ID()?.symbol.line;
            let refV = this.currentScope?.resolve(ctx.type()?.text as string);
            this.currentScope?.define(
                new _Symbol(
                    ctx.ID()?.text as string,
                    refV as Type
                )
            )
            console.log("line " + line + " field:", ctx.ID()?.text, refV?.name)
        }else if(ctx.structDeclaration()){
            // struct {int a;struct B {int z}}
            // enter StructDeclaration
        }
    }

    exitStructDeclaration(ctx:StructDeclarationContext){
        console.log("fields: ",this.currentScope?.toString());
        this.currentScope = (this.currentScope as Scope).getEnclosingScope()
    }

    enterClassDefinition(ctx:ClassDefinitionContext){
        let line = ctx.ID().symbol.line;
        console.log("line " + line + " def class " + ctx.ID().text);
        let superSymbol  = null;
        if(ctx.superClass()){
            let superName = ctx.superClass()?.ID().text;
            superSymbol = this.currentScope?.resolve(superName as string) as ClassSymbol;
            console.log("line " + line + " super class " + superName);
        }
        let cs = new ClassSymbol(
            ctx.ID().text,
            this.currentScope as Scope,
            superSymbol
        )
        this.currentScope?.define(cs);
        this.currentScope = cs;
    }

    enterClassMember(ctx:ClassMemberContext){
        if(ctx.ID()){
             // class A {int a}
             let line = ctx.ID()?.symbol.line;
             let refV = this.currentScope?.resolve(ctx.type()?.text as string);
             this.currentScope?.define(
                 new _Symbol(
                     ctx.ID()?.text as string,
                     refV as Type
                 )
             )
             console.log("line " + line + " members:", ctx.ID()?.text, refV?.name)
        }else if(ctx.methodDeclaration()){
            // enter methodDeclaration
        }
    }
    exitClassDefinition(ctx:ClassDefinitionContext){
        console.log("member: ",this.currentScope?.toString());
        this.currentScope = (this.currentScope as Scope).getEnclosingScope()
    }
}


// Create the listener
const listener = new myListener();
ParseTreeWalker.DEFAULT.walk(listener as ParseTreeListener, tree)
console.log(listener.symTab.toString());